
/**
  ******************************************************************************
  * Copyright 2021 The Microbee Authors. All Rights Reserved.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  * 
  * http://www.apache.org/licenses/LICENSE-2.0
  * 
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  * 
  * @file       notify_ncp5623.c
  * @author     baiyang
  * @date       2022-5-19
  ******************************************************************************
  */

/*----------------------------------include-----------------------------------*/
#include "notify_ncp5623.h"

#include <rtthread.h>
#include <rtdevice.h>

#if CONFIG_HAL_BOARD != HAL_BOARD_SITL_WIN

/*-----------------------------------macro------------------------------------*/
#define NCP5623_LED_BRIGHT  0x1f    // full brightness
#define NCP5623_LED_MEDIUM  0x18    // medium brightness
#define NCP5623_LED_DIM     0x0f    // dim
#define NCP5623_LED_OFF     0x00    // off

#define NCP5623_LED_I2C_ADDR 0x38    // default I2C bus address
#define NCP5623_C_LED_I2C_ADDR 0x39    // default I2C bus address for the NCP5623C

#define NCP5623_LED_PWM0    0x40    // pwm0 register
#define NCP5623_LED_PWM1    0x60    // pwm1 register
#define NCP5623_LED_PWM2    0x80    // pwm2 register
#define NCP5623_LED_ENABLE  0x20    // enable register
/*----------------------------------typedef-----------------------------------*/

/*---------------------------------prototype----------------------------------*/
static void bind_function(ncp5623_t ncp5623_led);
static bool hw_set_rgb(rgb_led_t led, uint8_t r, uint8_t g, uint8_t b);
static bool init(notify_device_t notify_dev);
static bool ncp5623_write(ncp5623_t ncp5623_led, uint8_t reg, uint8_t data);
static bool ncp5623_write_pwm(ncp5623_t ncp5623_led, uint8_t _rgb[3]);
static void _timer(void *parameter);
/*----------------------------------variable----------------------------------*/
static struct rgb_led_ops ncp5623_led_ops;
static struct notify_device_ops ncp5623_notify_dev_ops;
/*-------------------------------------os-------------------------------------*/

/*----------------------------------function----------------------------------*/
/**
  * @brief       
  * @param[in]   ncp5623_led  
  * @param[in]   bus  
  * @param[out]  
  * @retval      
  * @note        
  */
void ncp5623_ctor(ncp5623_t ncp5623_led, uint8_t bus)
{
    rgb_led_ctor(&ncp5623_led->backend_rgb, NCP5623_LED_OFF, NCP5623_LED_BRIGHT, NCP5623_LED_MEDIUM, NCP5623_LED_DIM);
    ncp5623_led->_bus = bus;

    bind_function(ncp5623_led);
}

notify_device_t ncp5623_probe(uint8_t bus)
{
    ncp5623_t ncp5623_led = (ncp5623_t)rt_malloc(sizeof(struct ncp5623));

    if (ncp5623_led) {
        rt_memset(ncp5623_led, 0, sizeof(struct ncp5623));
        ncp5623_ctor(ncp5623_led, bus);

        return (notify_device_t)ncp5623_led;
    }

    return NULL;
}

static void bind_function(ncp5623_t ncp5623_led)
{
    ncp5623_led_ops = rgb_led_ops();
    ncp5623_notify_dev_ops = rgb_led_notify_device_ops();

    ncp5623_led_ops.hw_set_rgb = hw_set_rgb;
    ncp5623_notify_dev_ops.init = init;

    ncp5623_led->backend_rgb.ops = &ncp5623_led_ops;
    ncp5623_led->backend_rgb.notify_dev.ops = &ncp5623_notify_dev_ops;
}

static bool hw_set_rgb(rgb_led_t led, uint8_t red, uint8_t green, uint8_t blue)
{
    ncp5623_t ncp5623_led = (ncp5623_t)(&led->notify_dev);

    ncp5623_led->rgb[0] = red;
    ncp5623_led->rgb[1] = green;
    ncp5623_led->rgb[2] = blue;

    ncp5623_led->_need_update = true;
    return true;
}

static bool init(notify_device_t notify_dev)
{
    ncp5623_t ncp5623_led = (ncp5623_t)notify_dev;

    uint8_t addrs[] = { NCP5623_LED_I2C_ADDR, NCP5623_C_LED_I2C_ADDR };

    for (uint8_t i=0; i<ARRAY_SIZE(addrs); i++) {
        // first look for led on external bus
        ncp5623_led->_dev = devmgr_get_i2c_device(ncp5623_led->_bus, addrs[i]);
        if (!ncp5623_led->_dev) {
            continue;
        }

        devmgr_take_bus(ncp5623_led->_dev);
        devmgr_set_retries(ncp5623_led->_dev, 10);

        // enable the led
        bool ret = ncp5623_write(ncp5623_led, NCP5623_LED_ENABLE, 0x1f);
        if (!ret) {
            devmgr_release_bus(ncp5623_led->_dev);
            continue;
        }

        // update the red, green and blue values to zero
        uint8_t off[3] = { ncp5623_led->backend_rgb._led_off, ncp5623_led->backend_rgb._led_off, ncp5623_led->backend_rgb._led_off };
        ret = ncp5623_write_pwm(ncp5623_led, off);

        devmgr_set_retries(ncp5623_led->_dev, 1);

        // give back i2c semaphore
        devmgr_release_bus(ncp5623_led->_dev);

        if (ret) {
            devmgr_register_periodic_callback(ncp5623_led->_dev, 20000, _timer, ncp5623_led);
        }
        return true;
    }

    return false;
}

static bool ncp5623_write(ncp5623_t ncp5623_led, uint8_t reg, uint8_t data)
{
    uint8_t msg[1] = { 0x00 };
    // scale to 0 to 0x1F
    data = (data * 255U) / 0x1FU;
    msg[0] = ((reg & 0xe0) | (data & 0x1f));
    bool ret = devmgr_transfer(ncp5623_led->_dev, msg, 1, NULL, 0);
    return ret;
}

static bool ncp5623_write_pwm(ncp5623_t ncp5623_led, uint8_t _rgb[3])
{
    uint8_t reg = NCP5623_LED_PWM0;
    for (uint8_t i=0; i<3; i++) {
        if (!ncp5623_write(ncp5623_led, reg+i*0x20, _rgb[i])) {
            return false;
        }
    }
    return true;
}

static void _timer(void *parameter)
{
    ncp5623_t ncp5623_led = (ncp5623_t)parameter;

    if (!ncp5623_led->_need_update) {
        return;
    }
    ncp5623_led->_need_update = false;

    ncp5623_write_pwm(ncp5623_led, ncp5623_led->rgb);
}

/*------------------------------------test------------------------------------*/

#endif

